package com.ruiysoft.service.order.facade;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.ruiysoft.controller.freight.InnrFreightApi;
import com.ruiysoft.controller.order.vo.OrderBuyerVO;
import com.ruiysoft.controller.order.vo.ShopVO;
import com.ruiysoft.entity.feight.Freight;
import com.ruiysoft.entity.feight.FreightRegion;
import com.ruiysoft.entity.mdse.MerchandiseSku;
import com.ruiysoft.entity.order.OrderMain;
import com.ruiysoft.entity.sys.SysMicroShop;
import com.ruiysoft.entity.sys.SysRegion;
import com.ruiysoft.pub.core.Result;
import com.ruiysoft.pub.core.ResultCode;
import com.ruiysoft.pub.core.ResultGenerator;
import com.ruiysoft.service.feight.FreightRegionService;
import com.ruiysoft.service.feight.IFreightService;
import com.ruiysoft.service.mdse.IMerchandiseSkuService;
import com.ruiysoft.service.mdse.IMerchandiseSpuService;
import com.ruiysoft.service.order.bean.OrderIntfResultDto;
import com.ruiysoft.service.order.bean.OrderPlaceSkuInfoVo;
import com.ruiysoft.service.sys.ISysRegionService;
import com.ruiysoft.service.sys.clent.LoginContextUtil;
import org.apache.commons.collections.map.HashedMap;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;

/**
 * Date : 2018/11/10
 *
 * @author WenJialu
 */
@Component
public class OrderCalculateFacade {

    @Resource
    private InnrFreightApi innrFreightApi;

    @Autowired
    private IFreightService freightService;

    @Autowired
    private ISysRegionService sysRegionService;

    @Autowired
    IMerchandiseSkuService merchandiseSkuService;

    Long getShopId() {
        SysMicroShop shop = LoginContextUtil.instance().getEntryMicroShop();
        return shop.getId();
    }


    Long freightGet(String postalCode, Long amount, List<Map<String, Object>> skuList) {
        Long famount = null;

        if(postalCode == null || postalCode == "") {
            System.out.println("postalCode 不能为空！");
            return null;
        }

        // 邮政编码换取地区编码
        SysRegion sysRegion = sysRegionService.getOne(new QueryWrapper<SysRegion>().eq("zipcode",postalCode), false);
        if(sysRegion != null)
            postalCode = sysRegion.getAdcode();

        Map retMap = new HashMap();
        // 从spu列表中挑出运费最贵的返回，应为客户只承担一个订单的运费。一个订单店铺可能要给多家供应商付运费

        Long shopId = getShopId();
        try {
            for (Map<String, Object> map: skuList) {
                Integer skuId = Integer.parseInt(map.get("skuId") + "");
                Integer quantity = (Integer) map.get("quantity");
                MerchandiseSku merchandiseSku = merchandiseSkuService.getById(skuId);

                Integer freightId = null;
                try {
                    freightId = Integer.parseInt(merchandiseSku.getFreightTemplateId());
                } catch (Exception error) {}

                if(freightId == null) {
                    System.out.println("运费模板未指定！");
                    return null;
                }

                List<Freight> freightList = freightService.freightList(shopId, freightId.longValue(),null,null);
                if(null != freightList && !freightList.isEmpty()){
                    Freight freight = freightList.get(0);
                    List<FreightRegion> freightRegionList = freight.getFreightRegionList();
                    if(null != freightRegionList && !freightRegionList.isEmpty()){
                        Map<String,FreightRegion> adMap = new HashMap<>();
                        for(FreightRegion freightRegion:freightRegionList){
                            adMap.put(freightRegion.getAdcode(),freightRegion);
                        }
                        FreightRegion freightRegion = null;
                        freightRegion = adMap.get(postalCode);
                        if(null == freightRegion){
                            freightRegion = adMap.get(postalCode.substring(0,4)+"00");
                        }
                        if(null == freightRegion){
                            freightRegion = adMap.get(postalCode.substring(0,3)+"000");
                        }
                        if(null == freightRegion){
                            freightRegion = adMap.get(postalCode.substring(0,2)+"0000");
                        }
                        if(null == freightRegion){
                            freightRegion = adMap.get(postalCode.substring(0,1)+"00000");
                        }
                        if(null != freightRegion){
                            String info = freight.getInfo();
                            freight = new Freight();
                            BeanUtils.copyProperties(freightRegion,freight);
                            freight.setInfo(info);
                        }
                    }

                    // 首件数量
                    Integer defaultCount = freight.getDefaultCount() != null ? freight.getDefaultCount() : 1;
                    // 首件运费
                    Integer defaultFee = freight.getDefaultFee();
                    // 续件数量
                    Integer increaseCount = freight.getIncreaseCount() != null ? freight.getIncreaseCount() : 1;
                    // 续件运费
                    Integer increaseFee = freight.getIncreaseFee() != null ? freight.getIncreaseFee() : 0;

                    famount = 0l;
                    // 整个订单金额大于满免金额，运费就是0元
                    if(amount != null && amount >= freight.getFullFee()){
                        famount = 0l;

                    } else {
                        if(quantity <= defaultCount){ //小于等于默认数量
                            famount = defaultFee.longValue();

                        } else {
                            // 计算续件数量
                            Integer leftCount = quantity - defaultCount;
                            Integer leftAmount = (leftCount%increaseCount) == 0 ? leftCount / increaseCount * increaseFee : (leftCount / increaseCount + 1) * increaseFee;
                            famount = new Long(defaultFee + leftAmount);
                        }
                    }
                    Long maxAmt = (Long)retMap.get("amount");
                    maxAmt = null == maxAmt?0l:maxAmt;
                    if(famount>=maxAmt) {
                        retMap.put("amount", famount);
                        retMap.put("freight", freight);
                    }
                }
            }

            return famount;

        } catch (Exception e) {
            System.out.println(e.getMessage());
            return null;
        }
    }

    // 后台计算运费，前台的只是用来显示
    public Long calculateFreight(OrderIntfResultDto orderIntfResultDto) {
        String postCode = orderIntfResultDto.getOrderReceiverInfoVo().getPostalCode();
        List<Map<String, Object>> listSkuObj = new ArrayList<>();
        for (OrderPlaceSkuInfoVo orderPlaceSkuInfoVo: orderIntfResultDto.getOrderPlaceSkuInfoVos()
             ) {
            Map<String, Object> map = new HashedMap();
            map.put("skuId", orderPlaceSkuInfoVo.getSkuId());
            map.put("quantity", orderPlaceSkuInfoVo.getQuantity());

            listSkuObj.add(map);
        }

        Long yunfei = freightGet(postCode, orderIntfResultDto.getOrderMain().getTotalAmount(), listSkuObj);
        if(yunfei != null)
            return yunfei;

        return 0l;
    }

    /*public Long calculateOrderTotalAmount(List<OrderPlaceSkuInfoVo> orderPlaceSkuInfoVos) {
        if (null == orderPlaceSkuInfoVos || orderPlaceSkuInfoVos.isEmpty()) {
            return NumberConst.ZERO_LONG;
        } else {
            return orderPlaceSkuInfoVos.stream()
                    .map(vo -> vo.getUnitPrice() * vo.getQuantity())
                    .reduce(0L, (a, b) -> a + b);
        }
    }*/

    public Result calculateOrderInPlace(ShopVO shopVO, OrderBuyerVO ownerVO, OrderIntfResultDto orderIntfResultDto) {
        Result result = calculateCashAndVirtualCoin(ownerVO, orderIntfResultDto);
        OrderMain orderMain = orderIntfResultDto.getOrderMain();
        Long mdseTotalAmount = orderMain.getMdseTotalAmount();

        Long freightAmount = calculateFreight(orderIntfResultDto);

        orderMain.setFreight(freightAmount);//运费
        orderMain.setReceivableAmount(Math.addExact(orderMain.getMdseReceivableAmount(), freightAmount));//商品现金总额+运费
        orderMain.setTotalAmount(Math.addExact(mdseTotalAmount, freightAmount));//商品价值总额+运费
        orderIntfResultDto.setTotalAmount(orderMain.getTotalAmount());
        orderIntfResultDto.setReceivableAmount(orderMain.getReceivableAmount());
        orderIntfResultDto.setFreight(freightAmount);
        orderIntfResultDto.setMdseTotalAmount(mdseTotalAmount);
        orderIntfResultDto.setMdseReceivableAmount(orderMain.getMdseReceivableAmount());
        orderIntfResultDto.setReceivableVirtualCoin(orderMain.getReceivableVirtualCoin());
        return result;
    }

    /**
     * 原地（in-place OrderIntfResultDto）计算
     *
     * @param ownerVO
     * @param orderIntfResultDto
     */
    public Result<Void> calculateCashAndVirtualCoin(OrderBuyerVO ownerVO, OrderIntfResultDto orderIntfResultDto) {
        Result<Void> result = new Result<>();
        result.setBizCode(ResultCode.CODE_SUCCESS);

        //// 类型（1，纯现金或纯积分，2，固定现金+固定积分，3，积分抵现金) ，需要删除魔幻数字
        AtomicLong ownedVirtualAmount = new AtomicLong(ownerVO.getVirtualCoin().orElse(0L));//拥有虚拟总额
        AtomicLong virtualAmount = new AtomicLong(0L);//虚拟支付总额
        //即使是负分，也必须计算。结果要反馈给前端。
        orderIntfResultDto.getOrderPlaceSkuInfoVos()
                .stream()
                .filter(c -> c.getPriceStrategy() != null && c.getPriceStrategy() == 2)//固定现金+固定积分
                .forEach(c ->
                        {
                            c.setVirtualCoin(Math.multiplyExact(c.getVirtualCoinPrice(), c.getQuantity()));
                            c.setTotalCash(Math.multiplyExact(c.getUnitPrice(), c.getQuantity()));
                            virtualAmount.addAndGet(c.getVirtualCoin());
                        }
                );
        orderIntfResultDto.getOrderPlaceSkuInfoVos()
                .stream()
                .filter(c -> c.getPriceStrategy() != null && c.getPriceStrategy() == 1)//单独现金或单独积分
                .forEach(c ->
                        {
                            Long usableCoin = ownedVirtualAmount.get() - virtualAmount.get();
                            if (usableCoin > 0) {
                                Long maxVirtualCount = Math.floorDiv(usableCoin, c.getVirtualCoinPrice());
                                Long virtualCount = Math.min(maxVirtualCount, c.getQuantity());
                                Long virtualCoin = Math.multiplyExact(virtualCount, c.getVirtualCoinPrice());
                                c.setVirtualCoin(virtualCoin);
                                c.setTotalCash(Math.multiplyExact(c.getUnitPrice(), c.getQuantity() - virtualCount));
                                virtualAmount.addAndGet(virtualCoin);
                            } else {
                                c.setVirtualCoin(0L);
                                c.setTotalCash(Math.multiplyExact(c.getUnitPrice(), c.getQuantity()));
                            }
                        }
                );
        orderIntfResultDto.getOrderPlaceSkuInfoVos()
                .stream()
                .filter(c -> c.getPriceStrategy() != null && c.getPriceStrategy() == 3)//积分可抵现金
                .forEach(c ->
                        {
                            Long usableCoin = ownedVirtualAmount.get() - virtualAmount.get();
                            if (usableCoin > 0) {
                                Long reqVirtualCoin = Math.multiplyExact(c.getVirtualCoinPrice(), c.getQuantity());//需要最多虚拟币
                                if (usableCoin >= reqVirtualCoin) {
                                    c.setVirtualCoin(reqVirtualCoin);
                                    c.setTotalCash(Long.valueOf(0L));
                                    virtualAmount.addAndGet(reqVirtualCoin);
                                } else {
                                    Long reqMaxCash = Math.multiplyExact(c.getUnitPrice(), c.getQuantity());//需要最多的现金
                                    Long offsetCash = Math.floorDiv(Math.multiplyExact(usableCoin, reqMaxCash), reqVirtualCoin);
                                    c.setVirtualCoin(usableCoin);
                                    c.setTotalCash(Math.subtractExact(Math.multiplyExact(c.getUnitPrice(), c.getQuantity()), offsetCash));//总现金-抵抗现金
                                    virtualAmount.addAndGet(usableCoin);
                                }
                            } else {
                                c.setVirtualCoin(0L);
                                c.setTotalCash(Math.multiplyExact(c.getUnitPrice(), c.getQuantity()));
                            }
                        }
                );

        AtomicLong totalAmount = new AtomicLong(0L);//商品总金额
        AtomicLong cashAmount = new AtomicLong(0L);//现金支付总额
        orderIntfResultDto.getOrderPlaceSkuInfoVos()
                .stream()
                .forEach(c ->
                        {
                            if (c.getTotalCash() == null) {
                                c.setTotalCash(Math.multiplyExact(c.getUnitPrice(), c.getQuantity()));
                            }
                            c.setTotalAmount(Math.multiplyExact(c.getUnitPrice(), c.getQuantity()));
                            totalAmount.addAndGet(c.getTotalAmount());
                            cashAmount.addAndGet(c.getTotalCash());
                        }
                );
        OrderMain orderMain = orderIntfResultDto.getOrderMain();
        orderMain.setMdseTotalAmount(totalAmount.get());//商品现金总价值
        orderMain.setMdseReceivableAmount(cashAmount.get());//商品应收现金总额
        orderMain.setReceivableVirtualCoin(virtualAmount.get());//商品应收虚拟总额
        if (ownedVirtualAmount.get() < virtualAmount.get()) {
            result
                    .setBizCode(ResultCode.CODE_COMMON_ERROR)
                    .setBizMessage(String.format("积分不足,剩余积分%d,需要积分%d", ownedVirtualAmount.get(), virtualAmount.get()));
        }
        return result;
    }

}
